一. 内部类
在 Java 中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
内部类包括四种:成员内部类、局部内部类、匿名内部类和静态内部类。
1. 成员内部类
第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
成员内部类是最普通的内部类,它的定义为位于另一个类的内部。
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
class Circle{
private double radius = 0;
public static int count = 1;
public Circle(double radius){
this.radius = radius;
}
class Draw{ // 内部类
public void drawShape(){
System.out.println(radius); // 外部类的private成员
System.out.println(count); // 外部类的静态成员
}
}
}
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件地访问外部类的成员,而外部类想访问成员内部类的成员却不是这么随心所欲了。在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问:
class Circle{
private double radius = 0;
public Circle(double radius){
this.radius = radius;
getDrawInstance().drawShape(); // 必须先创建成员内部类的对象,再进行访问
}
private Draw getDrawInstance(){
return new Draw();
}
class Draw{ // 内部类
public void drawShape(){
System.out.println(radius); // 外部类的private成员
}
}
}
成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象。创建成员内部类对象的一般方式如下:
package com.zth;
class Outter {
public Outter(){
}
class Inner{
public Inner(){
}
}
private Inner inner = null;
public Inner getInnerInstance(){
if (inner == null){
inner = new Inner();
}
return inner;
}
}
public class Test{
public static void main(String[] args){
// 方法一
Outter outter = new Outter();
Outter.Inner inner1 = outter.new Inner(); // 必须通过Outter对象来创建
// 方法二
Outter.Inner inner2 = outter.getInnerInstance();
}
}
推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。
2.局部内部类
局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。
package com.zth;
class People{
public People(){
}
}
class Man{
public Man(){
}
public People getWoman() {
class Woman extends People { // 局部内部类
int age = 0;
}
return new Woman();
}
}
注意: 局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
3.匿名内部类
a. 创建格式如下:
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
使用匿名内部类必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用 new 来生成一个对象的引用。当然这个引用是隐式的。
package com.zth;
abstract class Bird{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int fly();
}
public class Demo{
public void demo(Bird bird){
System.out.println(bird.getName()+"能够飞"+bird.fly()+"米");
}
public static void main(String[] args){
Demo demo = new Demo();
demo.demo(new Bird(){
public int fly(){
return 10000;
}
public String getName(){
return "大雁";
}
});
}
}
执行结果:
大雁能够飞10000米
在Test类中,test()方法接受一个 Bird 类型的参数,一个抽象类是没有办法直接new的,我们必须要先有实现类才能new出来它的实现类实例。所以在mian方法中直接使用匿名内部类来创建一个Bird实例。
由于匿名内部类不能是抽象类,所以它必须要实现它的抽象父类或者接口里面所有的抽象方法。
b. 注意事项
1、使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
2、匿名内部类中是不能定义构造函数的。
3、匿名内部类中不能存在任何的静态成员变量和静态方法。
4、匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
5、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
c. 形参必须为 final
当所在的方法的形参需要被内部类里面使用时,该形参必须为final。
简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变。
故如果定义了一个匿名内部类,并且希望它使用一个其外部定义的参数,那么编译器会要求该参数引用是final的。
4. 静态内部类
使用static修饰的内部类称之为静态内部类,也称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:
1、 它的创建是不需要依赖于外围类的。
2、 它不能使用任何外围类的非static成员变量和方法。
package com.zth;
public class OutClass{
private String sex;
public static String name = "zth";
/**
* 静态内部类
*/
static class InnerClass1{
public static String _name1 = "zth_static";
public void display(){
/*
* 静态内部类只能访问外围类的静态成员变量和方法
* 不能访问外围类的非静态成员变量和方法
*/
System.out.println("OutClass name: "+name);
}
}
/**
* 非静态内部类
*/
class InnerClass2{
// 非静态内部类中不能存在静态成员
public String _name2 = "zth_inner";
// 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的
public void display(){
System.out.println("OutClass name: "+name);
}
}
/**
* @author 时光·漫步
* 外围类方法
*/
public void display(){
/* 外围类访问静态内部类:内部类. */
System.out.println(InnerClass1._name1);
/* 静态内部类 可以直接创建实例不需要依赖于外围类 */
new InnerClass1().display();
/* 非静态内部的创建需要依赖于外围类 */
OutClass.InnerClass2 inner2 = new OutClass().new InnerClass2();
/* 方位非静态内部类的成员需要使用非静态内部类的实例 */
System.out.println(inner2._name2);
inner2.display();
}
public static void main(String[] args) {
OutClass outer = new OutClass();
outer.display();
}
}
二. 内部类的使用场景和好处
- 1.每个内部类都能独立的继承一个接口的实现,所以无论外部类是否已经继承了某个(接口的)实现,对于内部类都没有影响。内部类使得多继承的解决方案变得完整。
- 2.方便将存在一定逻辑关系的类组织在一起,又可以对外界隐藏。
- 3.方便编写事件驱动程序。
- 4.方便编写线程代码。
内部类实例化实例:
package com.zth;
public class Test{
public static void main(String[] args){
// 初始化 Bean1
Test test = new Test();
Test.Bean1 bean1 = test.new Bean1();
// 初始化 Bean2
Test.Bean2 bean2 = new Test.Bean2();
// 初始化 Bean3
Bean bean = new Bean();
Bean.Bean3 bean3 = bean.new Bean3();
}
class Bean1{
public int i = 0;
}
static class Bean2{
public int j = 0;
}
}
class Bean{
class Bean3{
public int k = 0;
}
}